package com.young.mybatis;

import com.young.config.Configuration;
import com.young.config.MappedStatement;
import com.young.mybatis.v2.sqlnode.IfSqlNode;
import com.young.mybatis.v2.sqlnode.MixedSqlNode;
import com.young.mybatis.v2.sqlnode.StaticTextSqlNode;
import com.young.mybatis.v2.sqlnode.TextSqlNode;
import com.young.mybatis.v2.sqlnode.iface.SqlNode;
import com.young.mybatis.v2.sqlsource.DynamicSqlSource;
import com.young.mybatis.v2.sqlsource.RawSqlSource;
import com.young.mybatis.v2.sqlsource.iface.SqlSource;
import com.young.mybatis.v2.sqlsource.model.BoundSql;
import com.young.mybatis.v2.sqlsource.model.ParameterMapping;
import org.apache.commons.dbcp.BasicDataSource;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描述:mybaits v2版
 *
 * @author BluceYoung
 * @create 2020-07-01 13:48
 * B站：TeacherYoung凌风
 * www.teacheryoung.com
 */
public class V2 {

    private Configuration configuration=new Configuration();
    private String namespace;
    private boolean isDynamic;

    public V2() {
        loadXML("mybatis-config.xml");
    }

    /**
     * 加载
     *
     * @param location
     */
    private void loadXML(String location) {
        InputStream inputStream = getResourceAsStream(location);
        Document document = getDocument(inputStream);//xml根节点
        parseConfiguration(document.getRootElement());
    }

    private void parseConfiguration(Element rootElement) {
        Element environments = rootElement.element("environments");

        parseEnvironment(environments);//挑一个env使用

        Element mappers = rootElement.element("mappers");
        parseMappers(mappers);
    }

    /**
     * 解析mappers节点
     *
     * @param mappersElement
     */
    private void parseMappers(Element mappersElement) {
        List<Element> mappers = mappersElement.elements("mapper");
        for (Element mapper : mappers) {//遍历所有的mapper节点
            String resource = mapper.attributeValue("resource");
            InputStream inputStream = getResourceAsStream(resource);
            Document document = getDocument(inputStream);
            parseMapper(document.getRootElement());
        }
    }

    /**
     * 解析mapper节点
     *
     * @param mapperElement
     */
    private void parseMapper(Element mapperElement) {
        this.namespace = mapperElement.attributeValue("namespace");
        List<Element> selectElements = mapperElement.elements("select");
        for (Element selectElement : selectElements) {
            parseStatementElement(selectElement);
        }

    }

    /**
     * 把一个select标签解析成一个Statement
     *
     * @param selectElement
     */
    private void parseStatementElement(Element selectElement) {
        String statementId = selectElement.attributeValue("id");
        if (statementId == null || selectElement.equals("")) {
            return;
        }
        String resultType = selectElement.attributeValue("resultType");
        Class<?> resultClass = resolveType(resultType);

        String statementType = selectElement.attributeValue("statementType");

        SqlSource sqlSource=createSqlSource(selectElement);

        MappedStatement mappedStatement=new MappedStatement(statementId,resultClass,statementType,sqlSource);
        configuration.addMappedStatement(statementId,mappedStatement);
    }

    private SqlSource createSqlSource(Element selectElement) {
        isDynamic=false;
        SqlSource sqlSource = parseScriptNode(selectElement);
        return sqlSource;
    }

    private SqlSource parseScriptNode(Element selectElement) {
        //解析动态标签
        SqlNode mixedSqlNode = parseDynamicTags(selectElement);

        SqlSource sqlSource;
        //如果有$或动态标签
        if(isDynamic){
            sqlSource=new DynamicSqlSource(mixedSqlNode);
        }else{

            sqlSource=new RawSqlSource(mixedSqlNode);
        }
        return sqlSource;
    }

    private SqlNode parseDynamicTags(Element selectElement) {
        List<SqlNode> sqlNodes=new ArrayList<SqlNode>();

        int nodeCount = selectElement.nodeCount();//文本标签和元素标签的总数量
        for (int i = 0; i < nodeCount; i++) {
            Node node=selectElement.node(i);
            if(node instanceof Text){
                String text=node.getText();
                if(text==null ){
                    continue;
                }
                if( "".equals(text.trim())){
                    continue;
                }
                TextSqlNode textSqlNode=new TextSqlNode(text.trim());
                if(textSqlNode.isDynamic()){
                    sqlNodes.add(textSqlNode);
                    isDynamic=true;
                }else{
                    sqlNodes.add(new StaticTextSqlNode(text.trim()));
                }
            }else if(node instanceof Element){
                Element element=(Element)node;
                String name = element.getName();
                if("if".equals(name)){
                    String test = element.attributeValue("test");
                    SqlNode sqlNode = parseDynamicTags(element);
                    IfSqlNode ifSqlNode=new IfSqlNode(test,sqlNode);
                    sqlNodes.add(ifSqlNode);
                }
            }else{

            }
        }
        return new MixedSqlNode(sqlNodes);
    }

    private Class<?> resolveType(String className) {
        try {
            Class<?> clazz = Class.forName(className);
            return clazz;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解析environments节点
     *
     * @param environments
     */
    private void parseEnvironment(Element environments) {
        String defaultValue = environments.attributeValue("default");
        List<Element> listEnvironment = environments.elements("environment");
        for (Element element : listEnvironment) {
            String id = element.attributeValue("id");
            if (defaultValue.equalsIgnoreCase(id)) {
                parseDataSource(element.element("dataSource"));
            }
        }

    }

    private void parseDataSource(Element dataSourceElement) {
        String type = dataSourceElement.attributeValue("type");
        if ("DBCP".equalsIgnoreCase(type)) {
            BasicDataSource dataSource = new BasicDataSource();
            HashMap<String, String> map = parseProperties(dataSourceElement);
            dataSource.setDriverClassName(map.get("driver"));
            dataSource.setUrl(map.get("url"));
            dataSource.setUsername(map.get("username"));
            dataSource.setPassword(map.get("password"));
            configuration.setDataSource(dataSource);
        }
    }

    /**
     * 把datasoure下面的属性转换成map
     *
     * @param dataSourceElement
     * @return
     */
    private HashMap<String, String> parseProperties(Element dataSourceElement) {
        HashMap<String, String> map = new HashMap<String, String>();
        List<Element> properties = dataSourceElement.elements("property");
        for (Element prop : properties) {
            map.put(prop.attributeValue("name"), prop.attributeValue("value"));
        }
        return map;
    }

    /**
     * 读取一个xml文件
     *
     * @param inputStream
     * @return
     */
    private Document getDocument(InputStream inputStream) {
        try {
            SAXReader saxReader = new SAXReader();
            return saxReader.read(inputStream);
        } catch (Exception ex) {

        }
        return null;
    }

    private InputStream getResourceAsStream(String location) {
        return this.getClass().getClassLoader().getResourceAsStream(location);
    }


    public <T> List<T> getList(String statementId, Object par) {
        //获取特定的statment信息
        MappedStatement mappedStatement = configuration.getMappedStatementById(statementId);

        List<T> list = new ArrayList<T>();
        Connection connection = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            //1.获取连接
            connection = getConnection();
            //2.获取sql
            BoundSql boundSql=getSql(mappedStatement,par);
            String sql = boundSql.getSql();

            //3.创建Statement
            stmt = createStatement(connection, sql, mappedStatement);

            //4.设置参数
            setParams(stmt, par,boundSql);

            //5.执行Statement
            rs = handleStatement(stmt);

            //6.处理结果集
            handleResultSet(rs, list, mappedStatement);

            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private <T> void handleResultSet(ResultSet rs, List<T> list, MappedStatement stmt) throws ClassNotFoundException, SQLException, IllegalAccessException, InstantiationException {
        //String className = stmt.getResultType();
        Class clazz =stmt.getResultClass();// Class.forName(className);

        while (rs.next()) {
            Object result = clazz.newInstance();
            ResultSetMetaData metaData = rs.getMetaData();//获取查询结果集的列
            for (int i = 1; i <= metaData.getColumnCount(); i++) {//遍历结果集的所有列，下标从1开始
                String colName = metaData.getColumnName(i);//这个列名就是类属性的名字
                try {
                    Field field = clazz.getDeclaredField(colName);
                    field.setAccessible(true);
                    field.set(result, rs.getObject(i));//强制为对象的属性赋值
                } catch (Exception e) {
                    //e.printStackTrace();
                }
            }
            list.add((T) result);
        }

    }

    private ResultSet handleStatement(Statement stmt) throws SQLException {
        if (stmt instanceof PreparedStatement) {
            PreparedStatement preparedStatement = (PreparedStatement) stmt;
            return preparedStatement.executeQuery();
        }
        return null;
    }

    private void setParams(Statement stmt, Object par, BoundSql boundSql) throws SQLException {
        if (stmt instanceof PreparedStatement) {

            PreparedStatement preparedStatement = (PreparedStatement) stmt;
            if (par instanceof Map) {//多参数
                Map paramMap=(Map)par;
                //TODO 结合#{}的处理
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
                for (int i=0;i<parameterMappings.size();i++) {
                    String name = parameterMappings.get(i).getName();
                    Object value=paramMap.get(name);
                    Class type = parameterMappings.get(i).getType();
                    if(type!=null){

                    }else{
                        preparedStatement.setObject(i+1,value);
                    }
                }
            } else {//单参数
                preparedStatement.setObject(1, par);
            }
        } else {

        }

    }

    private Statement createStatement(Connection connection, String sql, MappedStatement mappedStatement) throws SQLException {
        String statementType = mappedStatement.getStatementType();
        if (statementType.equalsIgnoreCase("prepared")) {
            return connection.prepareStatement(sql);
        } else if (statementType.equalsIgnoreCase("callable")) {
            return connection.prepareStatement(sql);
        } else {
            return connection.createStatement();
        }
    }

    private BoundSql getSql(MappedStatement mappedStatement, Object par) {
        SqlSource sqlSource = mappedStatement.getSqlSource();
        BoundSql boundSql = sqlSource.getBoundSql(par);
        return boundSql;
    }

    private Connection getConnection() {
        DataSource dataSource = configuration.getDataSource();
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}
